/* * Copyright 2013 eWidgetFX. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ewidgetfx.util; import javafx.collections.FXCollections; import javafx.collections.ObservableMap; import javafx.stage.Stage; import org.ewidgetfx.core.LaunchInfo; import org.ewidgetfx.core.Widget; import org.ewidgetfx.core.WidgetIcon; import org.ewidgetfx.core.WidgetProxy; import java.io.File; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.lang.reflect.Proxy; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; import org.apache.log4j.Logger; /** * * @author Carl Dea <carl.dea@gmail.com> * @since 1.0 */ public class WidgetLoaderImpl implements WidgetLoader { private static final Logger logger = Logger.getLogger(WidgetLoaderImpl.class); File widgetDir = null; public static final String JABS_DIRECTORY = "jabs"; public static final String LIBS_DIRECTORY = "libs"; public WidgetLoaderImpl(File widgetDir) { this.widgetDir = widgetDir; } @Override public File getDefaultWidgetDirectory() { return widgetDir; } @Override public Widget loadFile(File widgetJarFile, Stage containerStage, ClassLoader parentClassLoader) { Widget widgetProxy = null; if (getDefaultWidgetDirectory() != null && !getDefaultWidgetDirectory().exists()) { getDefaultWidgetDirectory().mkdirs(); } int numJars = 0; try { // get manifest entries JarFile jabFile = null; jabFile = new JarFile(widgetJarFile); String widgetTypeStr = jabFile.getManifest().getMainAttributes().getValue("widget-type"); if (widgetTypeStr == null) { System.out.println("Skipping: " + widgetJarFile.getName()); jabFile.close(); return null; } // load jar dependencies String classpathStr = jabFile.getManifest().getMainAttributes().getValue("Class-Path"); System.out.println("classpath = " + classpathStr); String[] jarsDeps = null; if (classpathStr != null) { jarsDeps = classpathStr.split("\\s+"); } // count the widget jar numJars++; numJars += jarsDeps.length; // put jars there first. installDependencies(jabFile, JABS_DIRECTORY, LIBS_DIRECTORY); URL[] urls = new URL[numJars]; for (int i = 0; i < jarsDeps.length; i++) { System.out.println("jar deps: " + jarsDeps[i]); File jarFileDep = new File(new File(".").getAbsoluteFile().getParentFile() + File.separator + jarsDeps[i]); urls[i] = jarFileDep.toURI().toURL(); } urls[urls.length - 1] = widgetJarFile.toURI().toURL(); if (LaunchInfo.LaunchType.WIDGET.toString().equalsIgnoreCase(widgetTypeStr)) { String clazzName = jabFile.getManifest().getMainAttributes().getValue("widget-execution-line"); WidgetUrlClassLoader child = new WidgetUrlClassLoader(urls, parentClassLoader); Class classToLoad = Class.forName(clazzName, true, child); widgetProxy = (Widget) classToLoad.newInstance(); widgetProxy = (Widget) Proxy.newProxyInstance(Widget.class.getClassLoader(), new Class<?>[]{Widget.class}, new WidgetProxy(widgetProxy)); String widgetName = jabFile.getManifest().getMainAttributes().getValue("widget-name"); String widgetVersion = jabFile.getManifest().getMainAttributes().getValue("widget-version"); String widgetDescr = jabFile.getManifest().getMainAttributes().getValue("widget-description"); String widgetVendor = jabFile.getManifest().getMainAttributes().getValue("widget-vendor"); String widgetVendorUrl = jabFile.getManifest().getMainAttributes().getValue("widget-vendor-url"); String widgetVendorEmail = jabFile.getManifest().getMainAttributes().getValue("widget-vendor-email"); String widgetDecoration = jabFile.getManifest().getMainAttributes().getValue("widget-decoration"); widgetProxy.setName(widgetName); widgetProxy.setVersion(widgetVersion); widgetProxy.setDescription(widgetDescr); widgetProxy.setVendor(widgetVendor); widgetProxy.setVendorUrl(widgetVendorUrl); widgetProxy.setVendorEmail(widgetVendorEmail); Widget.DECORATION decoration = null; try { widgetProxy.setDecoration(Widget.DECORATION.valueOf(widgetDecoration)); } catch (Exception e) { widgetProxy.setDecoration(Widget.DECORATION.NON_STAGED_UNDECORATED); } // allow widget to be removed. jabFile.close(); LaunchInfo info = new LaunchInfo(); info.setLaunchType(LaunchInfo.LaunchType.WIDGET); info.setExecutionLine(clazzName); info.setWidget(widgetProxy); // BEGINNING Widget lifecycle // 1. call buildWidgetIcon() WidgetIcon widgetIcon = widgetProxy.buildWidgetIcon(); // a reference to the widget widgetIcon.setWidgetFilename(widgetJarFile.getName()); final Widget w = widgetProxy; w.setWidgetIcon(widgetIcon); // 2. call startBackground() widgetProxy.startBackground(); widgetIcon.setOnMouseClicked(WidgetFactory.createLaunchWidgetEventHandler(containerStage, w)); } } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException e1) { logger.error("Exceptionw when loading Widgets ", e1); } return widgetProxy; } private void installDependencies(final JarFile jabFile, String widgetPath, String libsPath) throws IOException { // load jar dependencies String classpathStr = jabFile.getManifest().getMainAttributes().getValue("Class-Path"); String[] jarsDeps = null; if (classpathStr != null) { jarsDeps = classpathStr.split("\\s+"); } if (jarsDeps == null || jarsDeps.length == 0) { return; } final File widgetDir = new File(new File(".").getAbsoluteFile().getParentFile() + File.separator + widgetPath); final File libsDir = new File(new File(".").getAbsoluteFile().getParentFile() + File.separator + widgetPath + File.separator + libsPath); if (!widgetDir.exists()) { boolean created = widgetDir.mkdirs(); } if (!libsDir.exists()) { boolean created = libsDir.mkdirs(); } // extract jars from classpath. final List<String> deps = new ArrayList<>(); for (String fileName : jarsDeps) { String simpleName = null; int slashIndx = fileName.lastIndexOf("/"); if (slashIndx > -1) { simpleName = fileName.substring(slashIndx + 1); } // extract jar deps.add(simpleName); } Stream<JarEntry> files = jabFile.stream().filter((Predicate) (jarEntry) -> { JarEntry je = (JarEntry) jarEntry; return (!je.isDirectory() && deps.contains(je.getName())); }); files.forEach((JarEntry jarEntry) -> { File tmpJar = new File(libsDir.getAbsolutePath() + File.separator + jarEntry.getName()); if (tmpJar.exists()) { return; } System.out.println("Loading... " + tmpJar); java.io.InputStream is = null; // get the input stream java.io.FileOutputStream fos = null; try { is = jabFile.getInputStream(jarEntry); try { fos = new java.io.FileOutputStream(tmpJar); } catch (FileNotFoundException e) { logger.error("File not found ", e); } while (is.available() > 0) { // write contents of 'is' to 'fos' fos.write(is.read()); } // flush bytes to disk fos.flush(); fos.close(); is.close(); } catch (IOException e) { logger.error("IO exception ", e); } finally { try { fos.close(); is.close(); } catch (IOException e) { logger.error("Exception when finally ", e); fos = null; is = null; } } }); } @Override public ObservableMap<String, Widget> loadWidgets(File widgetDir, String filePattern, Stage containerStage, ClassLoader parentClassLoader) { ObservableMap<String, Widget> widgetMap = FXCollections.observableMap(new HashMap<String, Widget>()); File[] widgets = null; if (widgetDir.exists()) { widgets = widgetDir.listFiles((FilenameFilter) (dir, name) -> { Pattern p = Pattern.compile(filePattern); Matcher m = p.matcher(name); return m.matches(); }); if (widgets != null && widgets.length > 0) { for (File widget1 : widgets) { System.out.println(widget1.getName()); Widget widget = loadFile(widget1, containerStage, this.getClass().getClassLoader()); if (widget == null) { continue; } widgetMap.put(widget1.getName(), widget); System.out.println("loaded: " + widget1.getName()); } } } else { widgetDir.mkdirs(); } return widgetMap; } } class WidgetUrlClassLoader extends URLClassLoader { WidgetUrlClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } WidgetUrlClassLoader(URL[] urls) { super(urls); } @Override public void addURL(URL url) { super.addURL(url); } }